#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <limits.h>

#include <aura.h>
#include <aura_wchar.h>
#include "module.h"

#define IS_PATH_SEPARATOR(c) ((c) == ':')

CHAR *GetOnePath(CHAR *des, CHAR *src)
{
    CHAR *s;
    CHAR *d;
    s = src;
    d = des;

    while('\0' != *s){
        *d = *s;
        if(IS_PATH_SEPARATOR(*s)){
            break;
        }
        s++;
        d++;
    }
    *d = '\0';
    //Stop behind the separator
    if('\0' != *s)s++;
    return s;
}
#ifndef _android
#define LINUX_DEFAULT_SO_PATH ((CHAR *)"/lib:/usr/lib")
#else
#define LINUX_DEFAULT_SO_PATH ((CHAR *)"/lib:/system/lib")
#endif
Aura_Int32 GetNameFromPath(CHAR *pszFullPath, CHAR *pszModuleName, CHAR *pszSrcPath)
{
    CHAR pszPath[AURA_MAX_PATH];
    CHAR *pszNextPathPoint = NULL;

    pszNextPathPoint = pszSrcPath;
    do{
        pszNextPathPoint = GetOnePath(pszPath, pszNextPathPoint);
        strcat(pszPath, "/");
        strcat(pszPath, pszModuleName);
        PTRACE("try pszPath:%s.\n", pszPath);
        if (realpath(pszPath, pszFullPath) != NULL){
            return (Aura_Int32)pszFullPath;
        }
    }while('\0' != *pszNextPathPoint);

    return 0;
}

EXTERN_C Aura_Int32 GetModuleFullPath(CHAR *pszFullPath, CHAR *pszModuleName)
{
    assert(pszFullPath && pszModuleName);
#ifdef _android
    strcpy(pszFullPath, "/data/Elastos/");
    strcat(pszFullPath, pszModuleName);
    return (Aura_Int32)pszFullPath; 
#else
    //First get the moudle
    CHAR *pszSrcPath = NULL;
    int ret = 0;
    if (realpath(pszModuleName, pszFullPath) != NULL){
        return (Aura_Int32)pszFullPath;
    }

    //Try get the fullpath through linux default PATH
    ret = GetNameFromPath(pszFullPath, pszModuleName, LINUX_DEFAULT_SO_PATH);
    if(0 != ret){
        return ret;
    }

    //Try get the fullpath through "PATH"
    pszSrcPath = getenv("PATH");
    if(NULL == pszSrcPath){
        return 0;
    }

    ret = GetNameFromPath(pszFullPath, pszModuleName, pszSrcPath);

    return ret;
#endif
}

Aura_ECode CLoadedFile::Init(const Aura_PWChar wszFullPath, CModule * pModule)
{
    Aura_ECode ec = AURA_NOERROR;
    Aura_UInt32 len;
    CHAR pszFileFullPath[AURA_MAX_PATH];

    if(NULL == wszFullPath){
        PERROR("Error: init name invalid! file:%s. line:%d.\n", __FILE__, __LINE__);
        return AURA_E_INVALID_ARGUMENT;
    }

    UnicToAnsi(pszFileFullPath, wszFullPath);

    m_iFd = open(pszFileFullPath, O_RDONLY);
    if(-1 == m_iFd){
        PERROR("Error: can not open file: %S! file:%s. line:%d.\n", wszFullPath, __FILE__, __LINE__);
        return AURA_E_FILE_NOT_FOUND;
    }

    len = strlen(pszFileFullPath) + 1;
    pModule->m_wszFullPath = new Aura_WChar[len];
    if (!pModule->m_wszFullPath) {
        return AURA_E_OUT_OF_MEMORY;
    }

    AnsiToUnic(pModule->m_wszFullPath, pszFileFullPath);
    pModule->m_wszName = pModule->m_wszFullPath;

    m_uFileSize = (Aura_UInt32)lseek(m_iFd, 0, SEEK_END);
    PTRACE("loaded file name:%s, fd:%d, size:%d.\n", pszFileFullPath, m_iFd, m_uFileSize);
    lseek(m_iFd, 0, SEEK_SET);

    return ec;
}

CLoadedFile::~CLoadedFile()
{
    close(m_iFd);
    m_iFd = -1;
    m_uFileSize = 0;
    if(NULL != m_pBuffer){
        free(m_pBuffer);
    }
    m_pBuffer = NULL;
}

Aura_ECode CLoadedFile::Read(Aura_UInt32 offset, Aura_UInt32 size, Aura_PVoid *ppBuf,
                 Aura_Bool bUseInterBuf)
{
    Aura_Byte*  pBuf = NULL;
    Aura_UInt32 uSetoff = 0;
    Aura_UInt32 uReadSize = 0;

    if(NULL == ppBuf){
        PERROR("Error: read buffer invalid! file:%s. line:%d.\n", __FILE__, __LINE__);
        return AURA_E_INVALID_ARGUMENT;
    }

    if(0 == size){
        return AURA_NOERROR;
    }

    if(bUseInterBuf){
        m_pBuffer = realloc(m_pBuffer, size);
        if(NULL == m_pBuffer){
            PERROR("Error: out of memory! file:%s. line:%d. fd:%d.\n", __FILE__, __LINE__, m_iFd);
            return AURA_E_OUT_OF_MEMORY;
        }else {
            memset(m_pBuffer, 0, size);
            *ppBuf = pBuf = (Aura_Byte*)m_pBuffer;
        }
    } else {
        pBuf = (Aura_Byte*)*ppBuf;
        if(NULL == pBuf){
            PERROR("Error: read buffer invalid! file:%s. line:%d.\n", __FILE__, __LINE__);
            return AURA_E_INVALID_ARGUMENT;
        }
    }

    uSetoff = lseek(m_iFd, offset, SEEK_SET);
    if(uSetoff != offset){
        PERROR("Error: seek file failed! file:%s. line:%d. fd:%d.\n", __FILE__, __LINE__, m_iFd);
        return AURA_E_INVALID_OPERATION;
    }

    uReadSize = read(m_iFd, pBuf, size);
    if(size != uReadSize){
        PERROR("Error: read file failed! \n \
                file:%s. line:%d. \n \
                fd:%d. read size:%d. readed size:%d.\n",
          __FILE__, __LINE__, m_iFd, size, uReadSize);
        return AURA_E_INVALID_OPERATION;
    }

    return AURA_NOERROR;
}

//#define DEBUG_FILE
#ifdef DEBUG_FILE
int main(int argc, char argv[])
{
    Aura_ECode        ec = AURA_NOERROR;
    CLoadedFile  file;
    Aura_Byte         *pBuf = NULL;
    int i        = 0;


    ec = file.Init("./loop.exe");
    if(AURA_FAILED(ec)){
        printf("load file failed!\n");
        return 0;
    }

    if(100 > file.GetSize()) {
        printf("file if too small.\n");
        printf("Command: \"file filename\", \n \
               file size must larger than 100 bytes\n");
    }

    printf("First read\n");
    ec = file.Read(0, 50, (Aura_PVoid *)&pBuf);
    if(AURA_FAILED(ec)){
        printf("read file failed!\n");
        return 0;
    }
    printf("read succeed pBuf:%x\n", pBuf);
    for(i = 0; i < 50; i++){
        printf("%x ", pBuf[i]);
    }
    printf("\n");

    printf("Second read\n");
    pBuf = (Aura_Byte *)malloc(100);
    memset(pBuf, 0, 100);
    ec = file.Read(0, 50, (Aura_PVoid *)&pBuf, false);
    if(AURA_FAILED(ec)){
        printf("read file failed!\n");
       return 0;
    }
    for(i = 0; i < 50; i++){
        printf("%x ", pBuf[i]);
    }
    free(pBuf);
    printf("\n");

    printf("Third read\n");
    ec = file.Read(25, 25, (Aura_PVoid *)&pBuf);
    if(AURA_FAILED(ec)){
        printf("read file failed!\n");
        return 0;
    }
    for(i = 0; i < 25; i++){
        printf("%x ", pBuf[i]);
    }
    printf("\n");

    return 0;
}
#endif
